| @@ -1,49 +1,93 @@ | ||
| 1 | 1 | module Agents | 
| 2 | 2 | class QpxAgent < Agent | 
| 3 | 3 |  | 
| 4 | - default_schedule "every_10m" | |
| 4 | + cannot_receive_events! | |
| 5 | + default_schedule "every_12h" | |
| 6 | + | |
| 5 | 7 |  | 
| 6 | 8 | description <<-MD | 
| 7 | - The QpxExpressAgent will tell you the minimum airline prices between a pair of cities, and within a certain period of time. | |
| 9 | + The QpxExpressAgent will tell you airline prices between a pair of cities. The api limit is 50 requests/day. | |
| 10 | + | |
| 11 | + Follow their documentation here (https://developers.google.com/qpx-express/v1/prereqs#get-a-google-account) to retrieve an api key. | |
| 12 | + After you get to the google developer console, created a project, enabled qpx express api then you can choose `api key` credential to be created. | |
| 13 | + | |
| 14 | + `Origin` and `Destination` requires `airport code`. | |
| 15 | + | |
| 16 | + All the default options must exist. For `infantInSeatCount`, `infantInLapCount`, `seniorCount`, and `childCount`, leave them to the default value of `0` if its not necessary. | |
| 17 | + | |
| 18 | + Set `refundable` to `true` if you want ticket to be refundable. Make sure `date` is in this type of date format `YYYY-MO-DAY`. | |
| 19 | + | |
| 20 | + You can limit the number of `solutions` returned back. The first solution is the lowest priced ticket. Every detailed trip option details starts at `"slice"`. | |
| 21 | + MD | |
| 22 | + | |
| 23 | + event_description <<-MD | |
| 24 | + The event payload will have objects that contains valuable data like this | |
| 25 | + | |
| 26 | + "carrier": [ | |
| 27 | +            {  | |
| 28 | + "code": "B6", | |
| 29 | + "name": "Jetblue Airways Corporation" | |
| 30 | + } | |
| 31 | + ] | |
| 32 | + | |
| 33 | + "tripOption": [ | |
| 34 | + "saleTotal": "USD49.10" | |
| 35 | + "slice": [ | |
| 36 | + ... | |
| 37 | + ... | |
| 38 | +             "flight": { | |
| 39 | + "carrier": "B6", | |
| 40 | + "number": "833" | |
| 41 | + } | |
| 42 | + ] | |
| 43 | + ] | |
| 8 | 44 |  | 
| 9 | - Follow their introduction documentation here (https://developers.google.com/qpx-express/v1/prereqs#get-a-google-account) to retrieve an api key. | |
| 10 | - After you get to the google chrome console and enabled qpx express api, you can choose `api key` credential to be created. | |
| 11 | - For round trips please provide a `return_date`. | |
| 12 | 45 | MD | 
| 13 | 46 |  | 
| 14 | 47 | def default_options | 
| 15 | 48 |        { | 
| 16 | - 'qpx_api_key' => 'AIzaSyCMwV5ackABmIPX9pUgEPELXB_FiNKmem0', | |
| 17 | - 'date' => "2016-03-18", | |
| 18 | - 'origin' => "origin", | |
| 19 | - 'destination' => "destination", | |
| 20 | - 'return_date' => "2016-03-25" | |
| 49 | + 'qpx_api_key': '', | |
| 50 | + 'adultCount': 1, | |
| 51 | + 'origin': 'BOS', | |
| 52 | + 'destination': 'SFO', | |
| 53 | + 'date': '2016-04-11', | |
| 54 | + 'childCount': 0, | |
| 55 | + 'infantInSeatCount': 0, | |
| 56 | + 'infantInLapCount': 0, | |
| 57 | + 'seniorCount': 0, | |
| 58 | + 'solutions': 3, | |
| 59 | + 'refundable': false | |
| 21 | 60 | } | 
| 22 | 61 | end | 
| 23 | 62 |  | 
| 24 | 63 | def validate_options | 
| 25 | 64 | errors.add(:base, "You need a qpx api key") unless options['qpx_api_key'].present? | 
| 26 | - # errors.add(:base, "A origin must exist") unless options['origin'].present? | |
| 27 | - # errors.add(:base, "A destination must exist") unless options['destination'].present? | |
| 28 | - # errors.add(:base, "A date must exist") unless options['date'].present? | |
| 65 | + errors.add(:base, "Adult Count must exist") unless options['adultCount'].present? | |
| 66 | + errors.add(:base, "Origin must exist") unless options['origin'].present? | |
| 67 | + errors.add(:base, "Destination must exist") unless options['destination'].present? | |
| 68 | + errors.add(:base, "Date must exist") unless options['date'].present? | |
| 69 | + errors.add(:base, "Child Count") unless options['childCount'].present? | |
| 70 | + errors.add(:base, "Infant In Seat Count must exist") unless options['infantInSeatCount'].present? | |
| 71 | + errors.add(:base, "Infant In Lap Count") unless options['infantInLapCount'].present? | |
| 72 | + errors.add(:base, "Senior Count must exist") unless options['seniorCount'].present? | |
| 73 | + errors.add(:base, "Solutions must exist") unless options['solutions'].present? | |
| 74 | + errors.add(:base, "Refundable must exist") unless options['refundable'].present? | |
| 29 | 75 | end | 
| 30 | 76 |  | 
| 31 | 77 | def working? | 
| 32 | 78 | !recent_error_logs? | 
| 33 | 79 | end | 
| 34 | 80 |  | 
| 35 | -    HEADERS = {"Content-Type" => "application/json"} | |
| 36 | - | |
| 37 | 81 | def check | 
| 38 | -      hash = {:request=>{:passengers=>{:adultCount=>1}, :slice=>[{:origin=>"BOS", :destination=>"LAX", :date=>"2016-03-20"}, {:origin=>"LAX", :destination=>"BOS", :date=>"2016-03-20"}]}} | |
| 39 | - body = JSON.generate(hash) | |
| 40 | - request = HTTParty.post(event_url, :body => @body, :headers => HEADERS) | |
| 82 | +      post_params = {:request=>{:passengers=>{:kind=>"qpxexpress#passengerCounts", :adultCount=> interpolated["adultCount"], :childCount=> interpolated["childCount"], :infantInLapCount=>interpolated["infantInLapCount"], :infantInSeatCount=>interpolated['infantInSeatCount'], :seniorCount=>interpolated["seniorCount"]}, :slice=>[{:kind=>"qpxexpress#sliceInput", :origin=> interpolated["origin"].to_s , :destination=> interpolated["destination"].to_s , :date=> interpolated["date"].to_s }], :refundable=> interpolated["refundable"], :solutions=> interpolated["solutions"]}} | |
| 83 | + body = JSON.generate(post_params) | |
| 84 | +      request = HTTParty.post(event_url, :body => body, :headers => {"Content-Type" => "application/json"}) | |
| 41 | 85 | events = JSON.parse request.body | 
| 42 | 86 | create_event :payload => events | 
| 43 | 87 | end | 
| 44 | 88 |  | 
| 45 | 89 | def event_url | 
| 46 | -      endpoint = 'https://www.googleapis.com/qpxExpress/v1/trips/search?key' + "#{URI.encode(interpolated[:qpx_api_key].to_s)}" | |
| 90 | +      endpoint = 'https://www.googleapis.com/qpxExpress/v1/trips/search?key=' + "#{URI.encode(interpolated[:qpx_api_key].to_s)}" | |
| 47 | 91 | end | 
| 48 | 92 | end | 
| 49 | 93 | end | 
| @@ -1,30 +1,36 @@ | ||
| 1 | 1 | require 'rails_helper' | 
| 2 | 2 |  | 
| 3 | -describe Agents::QpxExpressAgent do | |
| 3 | +describe Agents::QpxAgent do | |
| 4 | 4 | before do | 
| 5 | 5 |  | 
| 6 | 6 | stub_request(:get, "https://www.googleapis.com/qpxExpress/v1/trips/search").to_return( | 
| 7 | -      :body => File.read(Rails.root.join("spec/data_fixtures/qpx_express.json")), | |
| 7 | +      :body => File.read(Rails.root.join("spec/data_fixtures/qpx.json")), | |
| 8 | 8 | :status => 200, | 
| 9 | 9 |        :headers => {"Content-Type" => "application/json"} | 
| 10 | 10 | ) | 
| 11 | 11 |  | 
| 12 | 12 |      @opts = { | 
| 13 | - "qpx_api_key" => '800deeaf-e285-9d62-bc90-j999c1973cc9' | |
| 13 | + 'qpx_api_key' => '800deeaf-e285-9d62-bc90-j999c1973cc9', | |
| 14 | + 'adultCount' => 1, | |
| 15 | + 'origin' => 'BOS', | |
| 16 | + 'destination' => 'SFO', | |
| 17 | + 'date' => '2016-04-11', | |
| 18 | + 'childCount' => 0, | |
| 19 | + 'infantInSeatCount' => 0, | |
| 20 | + 'infantInLapCount'=> 0, | |
| 21 | + 'seniorCount'=> 0, | |
| 22 | + 'refundable' => false, | |
| 23 | + 'solutions'=> 3 | |
| 14 | 24 | } | 
| 15 | 25 |  | 
| 16 | - @checker = Agents::QpxExpressAgent.new(:name => "tectonic", :options => @opts) | |
| 26 | + @checker = Agents::QpxAgent.new(:name => "tectonic", :options => @opts) | |
| 17 | 27 | @checker.user = users(:bob) | 
| 18 | 28 | @checker.save! | 
| 19 | 29 | end | 
| 20 | 30 |  | 
| 21 | 31 | describe '#helpers' do | 
| 22 | - it "should return the correct request header" do | |
| 23 | -      expect(@checker.send(:request_options)).to eq({:headers => {"Content-Type"=>"application/json"}}) | |
| 24 | - end | |
| 25 | - | |
| 26 | 32 | it "should generate the correct events url" do | 
| 27 | -      expect(@checker.send(:event_url)).to eq("https://www.googleapis.com/qpxExpress/v1/trips/search?key") | |
| 33 | +      expect(@checker.send(:event_url)).to eq("https://www.googleapis.com/qpxExpress/v1/trips/search?key=800deeaf-e285-9d62-bc90-j999c1973cc9") | |
| 28 | 34 | end | 
| 29 | 35 | end | 
| 30 | 36 |  | 
| @@ -37,11 +43,61 @@ describe Agents::QpxExpressAgent do | ||
| 37 | 43 | @checker.options['qpx_api_key'] = nil | 
| 38 | 44 | expect(@checker).not_to be_valid | 
| 39 | 45 | end | 
| 46 | + | |
| 47 | + it "should require adultCount" do | |
| 48 | + @checker.options['adultCount'] = nil | |
| 49 | + expect(@checker).not_to be_valid | |
| 50 | + end | |
| 51 | + | |
| 52 | + it "should require Origin" do | |
| 53 | + @checker.options['origin'] = nil | |
| 54 | + expect(@checker).not_to be_valid | |
| 55 | + end | |
| 56 | + | |
| 57 | + it "should require Destination" do | |
| 58 | + @checker.options['destination'] = nil | |
| 59 | + expect(@checker).not_to be_valid | |
| 60 | + end | |
| 61 | + | |
| 62 | + it "should require Date" do | |
| 63 | + @checker.options['date'] = nil | |
| 64 | + expect(@checker).not_to be_valid | |
| 65 | + end | |
| 66 | + | |
| 67 | + it "should require childCount" do | |
| 68 | + @checker.options['childCount'] = nil | |
| 69 | + expect(@checker).not_to be_valid | |
| 70 | + end | |
| 71 | + | |
| 72 | + it "should require Infant In Seat Count" do | |
| 73 | + @checker.options['infantInSeatCount'] = nil | |
| 74 | + expect(@checker).not_to be_valid | |
| 75 | + end | |
| 76 | + | |
| 77 | + it "should require Infant In Lab Count" do | |
| 78 | + @checker.options['infantInLapCount'] = nil | |
| 79 | + expect(@checker).not_to be_valid | |
| 80 | + end | |
| 81 | + | |
| 82 | + it "should require Senior Count" do | |
| 83 | + @checker.options['seniorCount'] = nil | |
| 84 | + expect(@checker).not_to be_valid | |
| 85 | + end | |
| 86 | + | |
| 87 | + it "should require Solutions" do | |
| 88 | + @checker.options['solutions'] = nil | |
| 89 | + expect(@checker).not_to be_valid | |
| 90 | + end | |
| 91 | + | |
| 92 | + it "should require Refundable" do | |
| 93 | + @checker.options['refundable'] = nil | |
| 94 | + expect(@checker).not_to be_valid | |
| 95 | + end | |
| 40 | 96 | end | 
| 41 | 97 |  | 
| 42 | 98 | describe '#check' do | 
| 43 | 99 | it "should check that initial run creates an event" do | 
| 44 | - @checker.memory[:last_updated_at] = '2016-03-15T14:01:05+00:00' | |
| 100 | + @checker.memory[:latestTicketingTime] = '2016-03-18T23:59-04:00' | |
| 45 | 101 |        expect { @checker.check }.to change { Event.count }.by(1) | 
| 46 | 102 | end | 
| 47 | 103 | end |